package com.bite.system.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bit.common.security.handler.ServiceException;
import com.bite.common.core.contants.Constants;
import com.bite.common.core.enums.ResultCode;
import com.bite.system.domain.exam.Exam;
import com.bite.system.domain.exam.ExamQuestion;
import com.bite.system.domain.exam.dto.ExamAddDTO;
import com.bite.system.domain.exam.dto.ExamEditDTO;
import com.bite.system.domain.exam.dto.ExamQueryDTO;
import com.bite.system.domain.exam.dto.ExamQuestionDTO;
import com.bite.system.domain.exam.vo.ExamDetailVO;
import com.bite.system.domain.exam.vo.ExamVO;
import com.bite.system.domain.question.Question;
import com.bite.system.domain.question.vo.QuestionVO;
import com.bite.system.manager.ExamCacheManager;
import com.bite.system.mapper.exam.ExamMapper;
import com.bite.system.mapper.exam.ExamQuestionMapper;
import com.bite.system.mapper.question.QuestionMapper;
import com.bite.system.service.exam.IExamService;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper,ExamQuestion> implements IExamService {

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;

    @Autowired
    private ExamCacheManager examCacheManager;


    /**
     * 获取竞赛列表  难点:实现分页  返回结果
     * @param examQueryDTO
     * @return
     */
    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        PageHelper.startPage(examQueryDTO.getPageNum(),examQueryDTO.getPageSize());
        List<ExamVO> examVOList = examMapper.selectExamList(examQueryDTO);//当前符合条件的
        return examVOList;
    }

    /**
     * 添加竞赛
     * @param examAddDTO
     * @return
     */
    @Override
    public String  add(ExamAddDTO examAddDTO) {
        //竞赛是否存在  时间是否符合要求
        checkExamSaveParams(examAddDTO,null);

        Exam exam = new Exam();
        BeanUtils.copyProperties(examAddDTO,exam);
        examMapper.insert(exam);
        //返回竞赛id,后续前端需要根据id来编辑 Srting类型
        return exam.getExamId().toString();
    }


    /**
     * 在竞赛中添加题目  竞赛id 题目id
     * @param examQuestionDTO
     * @return
     */
    @Override
    public boolean questionAdd(ExamQuestionDTO examQuestionDTO) {
        //竞赛是否存在
        Exam exam = getExam(examQuestionDTO.getExamId());
        //竞赛已经开始
        checkExam(exam);
        if (Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        //题目是否有
        Set<Long> questionIdSet = examQuestionDTO.getQuestionIdSet();
        if (CollectionUtil.isEmpty(questionIdSet)){
            return true;  //没有题目
        }

        //一次查询多条题目信息
        List<Question> questionList = questionMapper.selectBatchIds(questionIdSet);

        if (CollectionUtil.isEmpty(questionList)  || questionList.size() < questionIdSet.size()){
            //存在有不存在的数据
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXIST);
        }
        //插入数据
        //下面这部分太长了,和上面区分,提出去
        return insertExamQuestion(exam, questionIdSet);
    }

    /**
     * 编辑竞赛中题目信息-删除
     * @param examId
     * @param questionId
     * @return
     */
    @Override
    public int questionDelete(Long examId, Long questionId) {
        //判断资源是否存在
        Exam exam = getExam(examId);
        //判断是否能删除---已经开赛
        checkExam(exam);
        if (Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        //存在 可以被操作
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId)
                .eq(ExamQuestion::getQuestionId,questionId));

    }

    /**
     * 获取竞赛详情 竞赛基本信息+题目信息
     * @param examId
     */
    @Override
    public ExamDetailVO detail(Long examId) {
        ExamDetailVO examDetailVO = new ExamDetailVO();
        //竞赛基本信息-判断是否存在
        Exam exam = getExam(examId);
        //基本信息赋值
        BeanUtils.copyProperties(exam,examDetailVO);

        //竞赛题目信息
        //获取题目id
        List<ExamQuestion> examQuestionList = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                .select(ExamQuestion::getQuestionId)
                .eq(ExamQuestion::getExamId, examId)
                .orderByAsc(ExamQuestion::getQuestionOrder));

        //判断是否为空
        if (CollectionUtil.isEmpty(examQuestionList)){
            //响应返回竞赛基本信息
            return examDetailVO;
        }
        //到这里题目信息不为空
        //将题目id收集到一个集合里
        //map函数对流中的每个元素进行转化
        //tolist将收集到的元素放在一个List
        List<Long> questionIdList = examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();

        //select * from tb_question question_id in (1,2,3)
        //这里是*,但是我不需要全部
        List<Question> questionList = questionMapper.selectList(new LambdaQueryWrapper<Question>()
                .select(Question::getQuestionId, Question::getTitle, Question::getDifficulty)
                .in(Question::getQuestionId, questionIdList));
        //集合转化  question->questionVO
//        List<QuestionVO> questionVOList = new ArrayList<>();

        List<QuestionVO> questionVOList = BeanUtil.copyToList(questionList, QuestionVO.class);
        //赋值
        examDetailVO.setExamQuestionList(questionVOList);
        return examDetailVO;

    }

    /**
     * 编辑竞赛基本信息
     * @param examEditDTO
     * @return
     */
    @Override
    public int edit(ExamEditDTO examEditDTO) {
        Exam exam = getExam(examEditDTO.getExamId());
        //已经发布,不能操作
        if (Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        //竞赛已经开始,不能编辑
        checkExam(exam);
        //对标题,开始时间,结束时间判断,与add相同   标题把自己本身排除
        //examEditDTO父类是AddDTO,所以也可以调用check方法
        checkExamSaveParams(examEditDTO,examEditDTO.getExamId());


        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        return examMapper.updateById(exam);
    }


    /**
     * 删除竞赛
     * @param examId
     */
    @Override
    public int delete(Long examId) {
        //判断是否存在
        Exam exam = getExam(examId);
        //发布,不能操作
        if (Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        //是否开始
        checkExam(exam);
        //可以删除

        //不存在也不会报错
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId));

        return examMapper.deleteById(examId);

    }

    /**
     * 发布竞赛
     * @param examId
     */
    @Override
    public int publish(Long examId) {
        //判断前提
        //竞赛是否存在
        Exam exam = getExam(examId);

        //如果此时已经完赛,就不发布了
        if (exam.getEndTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }

        //题目是否存在
        //查询数量
        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        if (count == null || count <= 0){
            throw  new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
        //前提满足
        exam.setStatus(Constants.TRUE);

        //将已发布的竞赛存入redis  e:t:l  e:d:l  examId
        examCacheManager.addCache(exam);

        return examMapper.updateById(exam);

    }

    /**
     * 撤销发布
     * @param examId
     * @return
     */
    @Override
    public int cancelPublish(Long examId) {

        //前提
        Exam exam = getExam(examId);
        checkExam(exam);

        //如果此时已经完赛,就不发布了
        if (exam.getEndTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }


        //后续就可以更改
        exam.setStatus(Constants.FALSE);

        //从redis中清除
        examCacheManager.deleteCache(examId);
        return examMapper.updateById(exam);
    }





    /**
     * 检验竞赛标题是否存在,时间是否符合要求
     * 传入examId,防止不需要修改标题的情况
     * @param examSaveDTO
     * @param examId
     */
    private void checkExamSaveParams(ExamAddDTO examSaveDTO,Long examId) {
        List<Exam> examList = examMapper
                .selectList(new LambdaQueryWrapper<Exam>()
                        .eq(Exam::getTitle, examSaveDTO.getTitle())
                        .ne(examId!=null,Exam::getExamId,examId));  //排除自身影响
        if (!CollectionUtil.isEmpty(examList)){
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        //时间校验
        if (examSaveDTO.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_STSRT_TIME_BEFORE_CURRENT_TIME);
        }
        if (examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())){
            throw new ServiceException(ResultCode.EXAM_STSRT_TIME_BEFORE_CURRENT_TIME);
        }
    }

    //竞赛已经开始 -不能删除
    private void checkExam(Exam exam){
        if (exam.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    /**
     * 给竞赛添加题目
     * @param exam
     * @param questionIdSet
     */
    private boolean insertExamQuestion(Exam exam, Set<Long> questionIdSet) {
        int num = 1;
        //创建一个集合-saveBatch要用  存放新加入的数据
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        //对题目是否存在
        //这一遍循环和数据库交互了两次,太耦合  有没有批量,一次查询 批量插入
        for (Long questionId: questionIdSet){
//            Question question = questionMapper.selectById(questionId);
//            if (question == null){
//                throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXIST);
//            }
            //不为空,加入数据
            ExamQuestion examQuestion = new ExamQuestion();
            examQuestion.setExamId(exam.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestion.setQuestionOrder(num++);
            //将题目数据存入集合当中
            examQuestionList.add(examQuestion);
//            examQuestionMapper.insert(examQuestion);
        }
        //saveBatch mybatis-plus提供的统一增添的方法
        return saveBatch(examQuestionList);
    }

    //共同代码
    //判断竞赛是否存在
    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectById(examId);
        if (exam == null){
            throw new ServiceException(ResultCode.EXAM_NOT_EXIST);
        }
        return exam;
    }

}
